# 数据压缩与存档

# zlib --- 与 gzip 兼容的压缩
import zlib

# 计算 data 的 Adler-32 校验值
print(zlib.adler32(b'dajsi'))  # 101319180

# 压缩 data 中的字节，返回含有已压缩内容的 bytes 对象
c = zlib.compress(b'sfoasire')
print(c)  # b'x\x9c+N\xcbO,\xce,J\x05\x00\x0f9\x03]'

# 解压 data 中的字节，返回含有已解压内容的 bytes 对象
d = zlib.decompress(c)
print(d)  # b'sfoasire'

# 返回一个 压缩对象，用来压缩内存中难以容下的数据流
c_obj = zlib.compressobj(-1)
print(c_obj)  # <zlib.Compress object at 0x000002CB5ED4BB70>
c_com = c_obj.compress(b'asjd')
print(c_com)  # b'x\x9c'
c_flush = c_obj.flush()
print(c_flush)  # b'K,\xceJ\x01\x00\x04\x19\x01\xa3'
# c_copy = c_obj.copy()
# print(c_copy)
d_obj = zlib.decompressobj(10)
print(d_obj)  # <zlib.Decompress object at 0x00000246320D1030>
print(d_obj.unused_data)  # b''
print(d_obj.unconsumed_tail)  # b''
print(d_obj.eof)  # False
# print(d_obj.decompress(c))

# 计算 data 的 CRC (循环冗余校验) 值
print(zlib.crc32(b'asfdsafas', 10))  # 2780524220

# gzip 对 gzip 格式的支持
import gzip
import io
import itertools
import os

# 写入压缩文件
file_name = 'tst-tar.tar'
with gzip.open(file_name, 'wb') as f:
    with io.TextIOWrapper(f, encoding='utf8') as enc:
        enc.write('we can write anything in the file here.\n')
print(file_name, 'contains', os.stat(file_name).st_size, 'bytes')  # tst-tar.tar contains 79 bytes

# 读取文件
with gzip.open(file_name, 'rb') as f:
    file_content = f.read()
    print(file_content)  # b'we can write anything in the file here.\r\n'

# 多行写入
with gzip.open(file_name, 'wb') as output:
    with io.TextIOWrapper(output, encoding='utf-8') as enc:
        enc.writelines(
            itertools.repeat('JournalDev, same line again and again!.\n', 10)
        )
print(file_name, 'contains', os.stat(file_name).st_size, 'bytes')  # tst-tar.tar contains 80 bytes

# 压缩与解压文件
# 压缩并写入
with gzip.GzipFile(mode='wb', compresslevel=9, fileobj=open('tst-tar.tar.tar', 'wb')) as g:
    g.write(b't like y')

# 解压缩
with gzip.GzipFile(mode='rb', fileobj=open('tst-tar.tar.tar', 'rb')) as g:
    print(g.read())  # b't like y'

# 压缩数据与解压数据
s_in = b"Lots of content here"
s_out = gzip.compress(s_in)
print(
    s_out)  # b'\x1f\x8b\x08\x006\r\x1aa\x02\xff\xf3\xc9/)V\xc8OSH\xce\xcf+I\xcd+Q\xc8H-J\x05\x00~u \x8e\x14\x00\x00\x00'
print(gzip.decompress(s_out))  # b'Lots of content here'

# bz2 对 bzip2 压缩算法的支持
import bz2

data = b"""
Donec rhoncus quis sapien sit amet molestie. Fusce scelerisque vel augue
nec ullamcorper. Nam rutrum pretium placerat. Aliquam vel tristique lorem,
sit amet cursus ante. In interdum laoreet mi, sit amet ultrices purus
pulvinar a. Nam gravida euismod magna, non varius justo tincidunt feugiat.
Aliquam pharetra lacus non risus vehicula rutrum. Maecenas aliquam leo
felis. Pellentesque semper nunc sit amet nibh ullamcorper, ac elementum
dolor luctus. Curabitur lacinia mi ornare consectetur vestibulum.
"""
# 压缩
c = bz2.compress(data)
# 计算压缩率
print(len(data) / len(c))  # 1.5059880239520957

# 解压缩+
d = bz2.decompress(c)
print(data == d)  # True


# 使用增量压缩器进行压缩
def gen_data(chunks=10, chunksize=1000):
    for _ in range(chunks):
        yield b"z" * chunksize


# 压缩版
comp = bz2.BZ2Compressor()
out_C = b""
for chunk in gen_data():
    out_C += comp.compress(chunk)
out_C = out_C + comp.flush()
# 不压缩版
out = b""
for chunk in gen_data():
    out += chunk
# 计算压缩率
print(len(out) / len(out_C))  # 212.7659574468085

# 读写文件
with bz2.open('myfile2.bz2', 'wb') as f:
    f.write(data)

with bz2.open('myfile2.bz2', 'rb') as f:
    content = f.read()
print(data == content)  # True

# lzma 用 LZMA 算法压缩
import lzma

# 写文件
data = b'Insert Data Here'
with lzma.open('file.xz', 'w') as f:
    f.write(data)

with lzma.open('file.xz') as f:
    print(f.read())  # b'Insert Data Here'

# 压缩
c = lzma.compress(data)
# 解压
d = lzma.decompress(c)
print(d == data)  # True

# 增量压缩与解压
lzc = lzma.LZMACompressor()
out1 = lzc.compress(b'One\n')
out2 = lzc.compress(b'Two\n')
out3 = lzc.compress(b'Three\n')
out4 = lzc.flush()
print(b''.join([out1, out2, out3,
                out4]))  # b'\xfd7zXZ\x00\x00\x04\xe6\xd6\xb4F\x02\x00!\x01\x16\x00\x00\x00t/\xe5\xa3\x01\x00\rOne\nTwo\nThree\n\x00\x00\x00%-\x9bl\x05\x80\xbdS\x00\x01&\x0e\x08\x1b\xe0\x04\x1f\xb6\xf3}\x01\x00\x00\x00\x00\x04YZ'

my_filters = [
    {"id": lzma.FILTER_DELTA, "dist": 5},
    {"id": lzma.FILTER_LZMA2, "preset": 7 | lzma.PRESET_EXTREME},
]
with lzma.open("file.xz", "w", filters=my_filters) as f:
    f.write(b"blah blah blah")

# zipfile 使用ZIP存档
import zipfile

# 用 PyZipFile 将 本文件夹下 文件/路径名不为 （test 或 test结尾） 的文件归档压缩
zf = zipfile.PyZipFile('myprog.zip', mode='w')


def notests(s):
    fn = os.path.basename(s)
    return (not (fn == 'tst' or fn.endswith('test')))


zf.writepy('.', filterfunc=notests)

# ZipFile
# 写入
zf_file = zipfile.ZipFile('ZipFile.zip', 'w')
zf_file.write(r'ceshi.py')
zf_file.close()
zf_file = zipfile.ZipFile('ZipFile.zip')
print(zf_file.namelist())  # ['ceshi.py']

# 解压
zf_file.extract('ceshi.py', './extract/')
zf_file.extractall('./extract/')

print(zf_file.filename, zf_file.debug, zf_file.comment)  # ZipFile.zip 0 b''

# ZipInfo对象
zip_info = zf_file.infolist()
print(zip_info)  # [<ZipInfo filename='ceshi.py' filemode='-rw-rw-rw-' file_size=3977>]
r = zf_file.getinfo('ceshi.py')
print(r.is_dir())  # False
print(r.filename, r.date_time, r.compress_type, r.comment)  # ceshi.py (2021, 8, 10, 10, 8, 58) 0 b''
print(r.extra, r.create_system, r.create_version, r.extract_version)  # b'' 0 20 20
print(r.reserved, r.flag_bits, r.volume, r.internal_attr, r.external_attr)  # 0 0 0 0 2176188416
print(r.header_offset, r.CRC, r.compress_size, r.file_size)  # 0 2928851444 3977 3977

# tarfile 读写tar归档文件
import tarfile

tar = tarfile.open('tar_file.tar', 'w')
print(tar)  # <tarfile.TarFile object at 0x000001523C7AB438>
# 添加一个文件
tar.add('tst/bar', arcname='bar.txt')
# 添加一个目录
tar.add('tst1')
tar.close()

# 读取
tar = tarfile.open('tar_file.tar', 'r')
print(
    tar.getmembers())  # [<TarInfo 'bar.txt' at 0x1c205d5a110>, <TarInfo 'tst1' at 0x1c205d5a430>, <TarInfo 'tst1/bar' at 0x1c205d5a750>, <TarInfo 'tst1/bar1' at 0x1c205d5a818>, <TarInfo 'tst1/bar3' at 0x1c205d5a8e0>]

# TarInfo对象
t = tar.getmember('bar.txt')
print(t)  # <TarInfo 'bar.txt' at 0x210d6caa110>
print(t.name, t.size, t.mtime, t.mode, t.type)  # bar.txt 12 1628737653 438 b'0'
print(t.linkname, t.uid, t.gid, t.uname, t.gname)  # 0 0
print(t.isfile(), t.isreg(), t.isdir(), t.issym(), t.islnk())  # True True False False False
print(t.ischr(), t.isblk(), t.isfifo(), t.isdev())  # False False False False
